package net.borderwars.simulator;

import net.borderwars.simulator.events.BEvent;

import javax.swing.*;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import java.util.*;
import java.util.concurrent.PriorityBlockingQueue;


/**
 * @author Eric
 *         Date: Feb 2, 2003
 *         Time: 10:00:01 PM
 */
public class EventQueue {

    PriorityBlockingQueue<BEvent> pbq = new PriorityBlockingQueue<BEvent>();
    private EventModel eventModel = null;
    private Clock timeKeeper = new Clock();


    public ListModel getModel() {
        if (eventModel == null) {
            eventModel = new EventModel();
        }
        return (eventModel);
    }

    LinkedList<BEvent> unscheduledEvents = new LinkedList<BEvent>();

    public void invokeSoon(BEvent e) {
        synchronized (unscheduledEvents) {
            unscheduledEvents.add(e);
        }
    }

    public void placeEvent(BEvent e) {
        // System.err.println("Adding " + encoder.getName());
        if (eventThread != null && !Thread.currentThread().equals(eventThread)) {
            throw new UnsupportedOperationException("Can't allow multithreaded access to the queue");
        }

        pbq.add(e);
        if (eventModel != null) {
            eventModel.eventAdded(e);
        }
    }

    private Thread eventThread = null;

    public BEvent take() {
        if (eventThread == null) {
            eventThread = Thread.currentThread();
        }
        synchronized (unscheduledEvents) {
            if (unscheduledEvents.size() != 0) {
                BEvent rtn = unscheduledEvents.removeFirst();
                return (rtn);
            }
        }
        try {
            BEvent e = pbq.take();
            if (eventModel != null) {
                eventModel.eventRemoved(e);
            }
            timeKeeper.set(e.getTimeStamp().longValue());
            return (e);
        }
        catch (InterruptedException e1) {
            e1.printStackTrace();
        }
        return (null);
    }

    public void remove(BEvent e) {
        if (eventThread != null && !Thread.currentThread().equals(eventThread)) {
            throw new UnsupportedOperationException("Can't allow multithreaded access to the queue");
        }

        pbq.remove(e);
        if (eventModel != null) {
            eventModel.eventRemoved(e);
        }

    }

    public Clock getClock() {
        return timeKeeper;
    }


    public class EventModel implements ListModel {
        BEvent[] events = new BEvent[0];
        HashSet<ListDataListener> listeners = new HashSet<ListDataListener>();

        synchronized public int getSize() {
            return (events.length);
        }

        public Object getElementAt(int index) {
            return (events[index]);
        }

        public void addListDataListener(ListDataListener l) {
            listeners.add(l);
        }

        public void removeListDataListener(ListDataListener l) {
            listeners.remove(l);
        }


        public void eventAdded(BEvent e) {
            Adder a = new Adder(e);
            SwingUtilities.invokeLater(a);
        }

        public void eventRemoved(BEvent e) {
            Remover r = new Remover(e);
            SwingUtilities.invokeLater(r);
        }


        private void listChanged() {
            ListChanger lc = new ListChanger(events.length);
            SwingUtilities.invokeLater(lc);
        }

        public class ListChanger implements Runnable {
            int x = 0;

            public ListChanger(int x) {
                this.x = x;
            }

            public void run() {
                ListDataEvent lde = new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, 0, x);
                for (Iterator<ListDataListener> i = listeners.iterator(); i.hasNext();) {
                    ListDataListener listener = i.next();
                    listener.contentsChanged(lde);
                }
            }
        }

        public class Adder implements Runnable {
            BEvent e = null;

            public Adder(BEvent e) {
                this.e = e;
            }


            public void run() {
                BEvent newEvents[] = new BEvent[events.length + 1];
                System.arraycopy(events, 0, newEvents, 0, events.length);
                newEvents[events.length] = e;
                events = newEvents;

                Arrays.sort(events);
                listChanged();
            }
        }

        public class Remover implements Runnable {
            BEvent e = null;

            public Remover(BEvent e) {
                this.e = e;
            }

            public String arrayToString(Object data[]) {
                String rtn = "[";
                for (int i = 0; i < data.length; i++) {
                    Object o = data[i];
                    rtn += o + ",";
                }
                rtn += "]";
                return (rtn);
            }

            public void run() {

                TreeSet<BEvent> data = new TreeSet<BEvent>();
                for (int i = 0; i < events.length; i++) {
                    BEvent lvEvent = events[i];
                    if (!e.equals(lvEvent)) {
                        data.add(lvEvent);
                    }
                }
                BEvent newEvents[] = new BEvent[data.size()];
                int x = 0;
                for (Iterator<BEvent> i = data.iterator(); i.hasNext();) {
                    BEvent event = i.next();
                    newEvents[x] = event;
                    x++;
                }

                events = newEvents;
                listChanged();
            }
        }
    }
}

